/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.moderatorplugin;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Calendar;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.LayoutStyle;
import javax.swing.SwingWorker;
import javax.swing.ToolTipManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;

import com.pearreview.ui.BottomButtonPanel;
import com.pearreview.ui.FormLabel;
import com.pearreview.ui.HeadingLabel;
import com.pearreview.ui.ImagePreview;
import com.pearreview.ui.PearPanel;
import com.pearreview.ui.TriStateCheckBox;
import com.pearreview.ui.table.BooleanTableCellEditor;
import com.pearreview.ui.table.BooleanTableCellRenderer;
import com.pearreview.ui.table.DateCalendarTableCellRenderer;
import com.pearreview.ui.table.MeetingCheckBoxTableModel;
import com.pearreview.ui.table.ProtocolTableCellRenderer;
import com.pearreview.ui.table.DefaultAdvancedTableCellRenderer;
import com.pearreview.application.Messages;
import com.pearreview.application.SpringGlassPane;
import com.pearreview.util.ImageFileFilter;
import com.pearreview.util.review.Meeting;
import com.pearreview.util.review.Protocol;
import com.pearreview.util.review.Review;

public class InvitationsPanel extends PearPanel implements ModeratorPanel {
	private static final long serialVersionUID = -4139694313874358543L;
	private FormLabel invitationZipFolderLabel;
	private JRadioButton radioBtnAsZip;
	private JRadioButton radioBtnAsFolder;
	private JRadioButton radioBtnAsPdf;
	private FormLabel createLabel;
	private JButton createInvitations;
	private JLabel imageLabel;
	private JLabel image;
	private JCheckBox addImageCheckBox;
	private FormLabel meetingListLabel;
	private JTable meetingTable;
	private MeetingCheckBoxTableModel meetingsModel;
	private JScrollPane scroll;
	private TriStateCheckBox selectAllCheckbox;
	
	private JPanel newFormPanel;
	private HeadingLabel headingLabel;
	private File lastDir = null;
	private File lastImageDir = null;
	
	private ModeratorReviewFormValidator validator;
	private Review review;
	
	private SpringGlassPane glassPane;
	private BottomButtonPanel bottom;
	
	public final static int PDF_EXPORT_METHODE = 1;
	public final static int ZIP_EXPORT_METHODE = 2;
	public final static int DIRECTORY_EXPORT_METHODE = 3;
	
	
	public InvitationsPanel(SpringGlassPane sgp) {
		glassPane = sgp;
		initComponents();
	}
	
	public void setExportMethod(int method) {
		switch(method) {
		case DIRECTORY_EXPORT_METHODE:
			radioBtnAsFolder.setSelected(true);
			break;
		case ZIP_EXPORT_METHODE:
			radioBtnAsZip.setSelected(true);
			break;
		default:
			radioBtnAsPdf.setSelected(true);
			break;
		}
	}
	
	public int getExportMethod() {
		if(radioBtnAsZip.isSelected()) {
			return ZIP_EXPORT_METHODE;
		} else if(radioBtnAsFolder.isSelected()) {
			return DIRECTORY_EXPORT_METHODE;
		} else {
			return PDF_EXPORT_METHODE;
		}
	}
	
	private void initComponents() {
		
		setFont(new Font("Tahoma", Font.PLAIN, 12));
		
		invitationZipFolderLabel = new FormLabel(getLocalizedString("save"));
		radioBtnAsPdf = new JRadioButton();
		radioBtnAsZip = new JRadioButton();
		radioBtnAsFolder = new JRadioButton();
		createLabel = new FormLabel(getLocalizedString("create_now"));
		createInvitations = new JButton(); 
		imageLabel = new FormLabel();
		image = new FormLabel();
		addImageCheckBox = new JCheckBox();
		meetingListLabel = new FormLabel(getLocalizedString("meeting_list"));
		meetingTable = new JTable();
		scroll = new JScrollPane();
		selectAllCheckbox = new TriStateCheckBox();
		selectAllCheckbox.addActionListener(new SelectAllActionListener());
		
		
		//---- meetingList ----
		meetingsModel = new MeetingCheckBoxTableModel();
		meetingTable.setFont(getFont());
		meetingTable.setModel(meetingsModel);
		
		DateCalendarTableCellRenderer calendarRenderer = 
				new DateCalendarTableCellRenderer();
		meetingTable.addMouseListener(calendarRenderer);
		meetingTable.addMouseMotionListener(calendarRenderer);
		meetingTable.setDefaultRenderer(Calendar.class, calendarRenderer);
		
		ProtocolTableCellRenderer protocolRenderer = 
				new ProtocolTableCellRenderer();
		meetingTable.addMouseListener(protocolRenderer);
		meetingTable.addMouseMotionListener(protocolRenderer);
		meetingTable.setDefaultRenderer(Protocol.class, protocolRenderer);
		
		DefaultAdvancedTableCellRenderer defaultRenderer = 
				new DefaultAdvancedTableCellRenderer();
		meetingTable.addMouseListener(defaultRenderer);
		meetingTable.addMouseMotionListener(defaultRenderer);
		meetingTable.setDefaultRenderer(String.class, defaultRenderer);
		
		BooleanTableCellRenderer booleanRenderer = 
				new BooleanTableCellRenderer();
		meetingTable.addMouseListener(booleanRenderer);
		meetingTable.addMouseMotionListener(booleanRenderer);
		meetingTable.setDefaultRenderer(Boolean.class, booleanRenderer);
		
		meetingTable.setDefaultEditor(Boolean.class,
				new BooleanTableCellEditor());
		
		TableColumn checkColumn = meetingTable.getColumnModel().getColumn(0);
		checkColumn.setResizable(false);
		checkColumn.setMaxWidth(checkColumn.getPreferredWidth());
		meetingTable.getTableHeader().setReorderingAllowed(false);
		scroll.setOpaque(false);
		scroll.getViewport().setOpaque(false);
		scroll.setViewportView(meetingTable);
		scroll.setBorder(BorderFactory.createMatteBorder(
				1, 1, 1, 1, new Color(0x828790)));
		meetingTable.setOpaque(false);
		meetingTable.setSelectionBackground(new Color(0xbfd72b));
		meetingTable.getModel()
				.addTableModelListener(new MeetinTableListener());
		
		//---- selectAll ----
		selectAllCheckbox.setOpaque(false);
		selectAllCheckbox.setFont(getFont());
		selectAllCheckbox.setText(getLocalizedString("select_all"));
		
		//---- radioBtnAsPdf ----
		radioBtnAsPdf.setText(getLocalizedString("as_pdf"));
		radioBtnAsPdf.setFont(getFont());
		radioBtnAsPdf.setOpaque(false);
		radioBtnAsPdf.setSelected(true);
		
		//---- radioBtnRefAsText ----
		radioBtnAsZip.setText(getLocalizedString("as_zip"));
		radioBtnAsZip.setFont(getFont());
		radioBtnAsZip.setOpaque(false);
		
		//---- radioBtnAsFolder ----
		radioBtnAsFolder.setText(getLocalizedString("as_dir"));
		radioBtnAsFolder.setFont(getFont());
		radioBtnAsFolder.setOpaque(false);
		//radioBtnAsFolder.addActionListener(this);
		
		//--- groupRadioButtons ---
		//Group the radio buttons.
		ButtonGroup radioGroup = new ButtonGroup();
		radioGroup.add(radioBtnAsPdf);
		radioGroup.add(radioBtnAsZip);
		radioGroup.add(radioBtnAsFolder);
		
		//---- createInvitations ----
		createInvitations.setAction(new CreateInvitationsAction());
		createInvitations.setText(getLocalizedString("create_invitations"));
		createInvitations.setFont(getFont());
		
		ToolTipManager.sharedInstance().registerComponent(image);
		
		imageLabel.setText(getLocalizedString("company_logo"));
		addImageCheckBox.setOpaque(false);
		addImageCheckBox.setText(getLocalizedString("add_logo"));
		addImageCheckBox.setFont(getFont());
		
		addImageCheckBox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(!addImageCheckBox.isSelected()) {
					image.setText(null);
					image.setToolTipText(null);
					return;
				}
				addImageCheckBox.setSelected(false);
				
				JFileChooser openDialog = new JFileChooser();
				openDialog.setCurrentDirectory(lastImageDir);
				if(lastImageDir != null) {
					openDialog.setSelectedFile(lastImageDir);
				}
				//Add a custom file filter and disable the default
				//(Accept All) file filter.
				openDialog.addChoosableFileFilter(new ImageFileFilter());
				openDialog.setAcceptAllFileFilterUsed(false);

				//Add the preview pane.
				openDialog.setAccessory(new ImagePreview(openDialog));
				glassPane.setFaded(true);
				if (JFileChooser.APPROVE_OPTION == 
						openDialog.showOpenDialog(InvitationsPanel.this)) {
					File f = openDialog.getSelectedFile();
					
					if (f.isFile() && f.canRead()) {
						lastImageDir = f;
						image.setText(f.getName());
						image.setToolTipText(f.toString());
						addImageCheckBox.setSelected(true);
					}
				}
				glassPane.setFaded(false);
			}
		});
		
		this.setLayout(new BorderLayout());
		
		setBackground(new Color(214, 214, 214));
		
		newFormPanel = new JPanel();
		newFormPanel.setOpaque(false);

		GroupLayout newLayout = new GroupLayout(newFormPanel);
		
		newFormPanel.setLayout(newLayout);
		newLayout.setHorizontalGroup(
				newLayout.createSequentialGroup()
					.addContainerGap()
					.addGroup(newLayout.createParallelGroup()
						.addGroup(newLayout.createSequentialGroup()
							.addGroup(newLayout.createParallelGroup()
								.addComponent(meetingListLabel, 
										GroupLayout.Alignment.LEADING, 
										80, 
										80, 
										GroupLayout.PREFERRED_SIZE)
								.addComponent(invitationZipFolderLabel, 
										GroupLayout.Alignment.LEADING, 
										80, 
										80, 
										GroupLayout.PREFERRED_SIZE)
								.addComponent(imageLabel,
										GroupLayout.Alignment.LEADING, 
										80, 
										80, GroupLayout.PREFERRED_SIZE)
								.addComponent(createLabel, 
										GroupLayout.Alignment.LEADING, 
										80, 
										80, 
										GroupLayout.PREFERRED_SIZE))
							.addGap(57, 57, 57)
							.addGroup(newLayout.createParallelGroup()
								.addGroup(newLayout.createSequentialGroup()
									.addGroup(newLayout.createParallelGroup(
												GroupLayout.Alignment.TRAILING, 
												false)
										.addComponent(createInvitations, 
												GroupLayout.Alignment.LEADING, 
												GroupLayout.DEFAULT_SIZE, 
												GroupLayout.PREFERRED_SIZE, 
												Short.MAX_VALUE)
										.addComponent(addImageCheckBox, 
												GroupLayout.Alignment.LEADING,
												GroupLayout.DEFAULT_SIZE, 
												GroupLayout.PREFERRED_SIZE, 
												Short.MAX_VALUE))
										.addGap(18, 18, 18)
										.addComponent(image))
								.addGroup(newLayout
										.createSequentialGroup()
										.addComponent(radioBtnAsPdf, 
												GroupLayout.PREFERRED_SIZE, 
												145, 
												GroupLayout.PREFERRED_SIZE)
										.addGap(10, 10, 10)
										.addComponent(radioBtnAsZip,
												GroupLayout.PREFERRED_SIZE, 
												145, 
												GroupLayout.PREFERRED_SIZE)
										.addGap(10, 10, 10)
										.addComponent(radioBtnAsFolder, 
												GroupLayout.PREFERRED_SIZE, 
												145, 
												GroupLayout.PREFERRED_SIZE))
									
								.addComponent(selectAllCheckbox,
										GroupLayout.Alignment.LEADING,
										GroupLayout.DEFAULT_SIZE, 
										GroupLayout.PREFERRED_SIZE, 
										GroupLayout.PREFERRED_SIZE)
								.addComponent(scroll,
										GroupLayout.DEFAULT_SIZE, 
										450, 
										GroupLayout.PREFERRED_SIZE))))
								
		);
		newLayout.setVerticalGroup(newLayout.createParallelGroup()
				.addGroup(newLayout.createSequentialGroup()
					.addContainerGap()
					.addGroup(newLayout.createParallelGroup()
						.addComponent(meetingListLabel)
						.addComponent(scroll, 
								GroupLayout.PREFERRED_SIZE, 
								150, 
								GroupLayout.PREFERRED_SIZE))		
					.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
					.addGroup(newLayout.createParallelGroup()
						.addComponent(selectAllCheckbox))
					.addGap(20)
					.addGroup(newLayout.createParallelGroup(
							GroupLayout.Alignment.BASELINE)
						.addComponent(invitationZipFolderLabel)
						.addComponent(radioBtnAsZip, 
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								GroupLayout.PREFERRED_SIZE)
						.addComponent(radioBtnAsFolder, 
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								Short.MAX_VALUE)
						.addComponent(radioBtnAsPdf, 
								GroupLayout.PREFERRED_SIZE, 
								GroupLayout.DEFAULT_SIZE, 
								Short.MAX_VALUE))
					.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
					.addGroup(newLayout.createParallelGroup(
							GroupLayout.Alignment.BASELINE)
						.addComponent(imageLabel)
						.addComponent(addImageCheckBox)
						.addComponent(image))
					.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
					.addGroup(newLayout.createParallelGroup(
							GroupLayout.Alignment.BASELINE)
						.addComponent(createLabel)
						.addComponent(createInvitations)))
		);
		
		
		//======== formPanel ========
		add(newFormPanel, BorderLayout.CENTER);
		headingLabel = new HeadingLabel(
				Messages.getString("PearReview.Moderator.tabs.invitations"));
		add(headingLabel, BorderLayout.NORTH);
		
		bottom = new BottomButtonPanel();
		add(bottom, BorderLayout.SOUTH);
	}
	
	public void setReviewValidator(ModeratorReviewFormValidator validator) {
		this.validator = validator;
	}
	
	/**
	 * Checks if image should be added to invitations
	 * @return <code>true</code> if image should be added, 
	 * otherwise <code>false</code>
	 */
	public boolean isImageAdded() {
		return addImageCheckBox.isSelected();
	}
	
	public void setImageAdded(boolean selected) {
		if(selected && !addImageCheckBox.isSelected() && lastImageDir == null) {
			throw new IllegalArgumentException("image file is null");
		}
		addImageCheckBox.setSelected(selected);
		
		image.setText(selected ? lastImageDir.getName() : null);
		image.setToolTipText(selected ? lastImageDir.toString() : null);
	}
	
	/**
	 * Returns the image file
	 * @return returns image file if added, otherwise <code>null</code>
	 */
	public File getImageFile() {
		return lastImageDir;
	}
	
	/**
	 * Sets the image file
	 */
	public void setImageFile(File file) {
		lastImageDir = file;
		
		if(file == null) {
			setImageAdded(false);
		} else if(isImageAdded()) {
			image.setText(lastImageDir.getName());
			image.setToolTipText(lastImageDir.toString());
		}
	}
	
	public void setReview(Review review) {
		this.review = review;
		
		boolean selectAll = meetingsModel.getMeetings() == null;
		List<Meeting> selectedMeetings = 
				meetingsModel.getSelectedMeetings();
		meetingsModel.setMeetings(review.getMeetings());
		
		if(selectAll) {
			meetingsModel.selectAll();
		}
		else {
			meetingsModel.setSelectedMeetings(selectedMeetings);
		}
	}
	
	/**
	 * If true, sets the loading panel with animation to the bottom of the 
	 * current panel and removes instead the bottom navigation buttons.
	 * 
	 * Otherwise removes the loading panel and puts the navigation buttons
	 * back.
	 * 
	 * @param loading
	 */
	private void setLoading(boolean loading) {
		if (loading) {
			//if (loadingPanel == null)
			//	loadingPanel = new LoadingPanel();
			//remove(bottom);
			//add(loadingPanel, BorderLayout.SOUTH);
			
			glassPane.setLoadingMessage(getLocalizedString("creating"));
			glassPane.showLoadingPanel(true);
		} else {
			//if (loadingPanel != null)
			//	remove(loadingPanel);
			//add(bottom, BorderLayout.SOUTH);
			glassPane.showLoadingPanel(false);
		}
		//repaint();
		//validate();
	}
	
	private class SelectAllActionListener implements ActionListener, 
			Serializable {
		private static final long serialVersionUID = -5564353656193248100L;
		@Override
		public void actionPerformed(ActionEvent e) {
			if(selectAllCheckbox.getState()
					.equals(TriStateCheckBox.State.CHECKED)) {
				meetingsModel.selectAll();
			} else if(selectAllCheckbox.getState()
					.equals(TriStateCheckBox.State.UNCHECKED)) {
				meetingsModel.clearSelection();
			}
		}
	}
	
	private class MeetinTableListener implements TableModelListener {
		@Override
		public void tableChanged(TableModelEvent e) {
			int count = meetingsModel.getSelectedCount();
			if(count == 0) {
				selectAllCheckbox.setState(TriStateCheckBox.State.UNCHECKED);
			} else if(count == meetingsModel.getRowCount()) {
				selectAllCheckbox.setState(TriStateCheckBox.State.CHECKED);
			} else {
				selectAllCheckbox.setState(TriStateCheckBox.State.PARTIAL);
			}
		}
	}
	
	private static String getLocalizedString(String key) {
		return Messages.getString("PearReview.Moderator.invitations." + key);
	}
	
	private class CreateInvitationsAction extends AbstractAction {
		private static final long serialVersionUID = 4198097882214324159L;

		@Override
		public void actionPerformed(ActionEvent e) {
			if(validator == null || !validator.isValidReviewForm()) {
				if(validator != null)
					validator.validateReviewForm();
				
				String message = getLocalizedString("error.validation");
				String title = getLocalizedString("error.validation.title");
				glassPane.setFaded(true);
				JOptionPane.showMessageDialog(InvitationsPanel.this, 
							message, 
							title, 
							JOptionPane.ERROR_MESSAGE);
				glassPane.setFaded(false);
				
				return;
			}
			
			final java.util.List<Meeting> selectedMeetings = 
					meetingsModel.getSelectedMeetings();
			
			if(selectedMeetings == null || selectedMeetings.size() <= 0) {
				String message = getLocalizedString("warning.meeting");
				String title = getLocalizedString("warning.meeting.title");
				glassPane.setFaded(true);
				JOptionPane.showMessageDialog(InvitationsPanel.this, 
						message, 
						title, 
						JOptionPane.WARNING_MESSAGE);
				glassPane.setFaded(false);
				return;
			}
			
			JFileChooser saveDialog = new JFileChooser();
			saveDialog.setFileSelectionMode(
					JFileChooser.DIRECTORIES_ONLY);
			saveDialog.setAcceptAllFileFilterUsed(false);
			saveDialog.setCurrentDirectory(lastDir);
			
			if(lastDir != null) {
				saveDialog.setSelectedFile(lastDir);
			}
			
			// TODO if user type a available file, the file chooser 
			// means that user cancel the dialog (CANCEL_OPTION)
			glassPane.setFaded(true);
			int ret = saveDialog.showSaveDialog(InvitationsPanel.this);
			glassPane.setFaded(false);
			
			if (JFileChooser.APPROVE_OPTION != ret)
				return;
			
			lastDir = saveDialog.getSelectedFile();
			
			final InvitationDocumentCreator docCreator;
			final InvitationPackageCreator packageCreator;
			final File imageFile = isImageAdded() ? getImageFile() : null;
			
			if(radioBtnAsZip.isSelected()) {
				docCreator = new PDFInvitationDocumentCreator(false, 
						imageFile);
				packageCreator = new ZipInvitationPackageCreator(true);
			} else if(radioBtnAsFolder.isSelected()) {
				docCreator = new PDFInvitationDocumentCreator(false, 
						imageFile);
				packageCreator = new DirectoryInvitationPackageCreator(true);
			} else {
				docCreator = new PDFInvitationDocumentCreator(true, 
						imageFile);
				packageCreator = new DirectoryInvitationPackageCreator(false);
			}

			SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
				@Override
				protected Void doInBackground() throws Exception {
					try {
						setLoading(true);
						packageCreator.create(review, 
								selectedMeetings,
								lastDir,
								docCreator);
						try {
							Desktop.getDesktop().browse(lastDir.toURI());
						} catch(IOException e1) {
							JOptionPane.showMessageDialog(InvitationsPanel.this,
									e1.getMessage(), 
									Messages.getString("PearReview.error"), 
									JOptionPane.ERROR_MESSAGE);
						}
						
					} catch (Exception e1) {
						JOptionPane.showMessageDialog(InvitationsPanel.this,
								e1.getMessage(), 
								Messages.getString("PearReview.error"), 
								JOptionPane.ERROR_MESSAGE);
					} finally {
						setLoading(false);
					}
					return null;
				}
			};
			worker.execute();
		}
	}

	@Override
	public String getHint() {
		return Messages.getString("PearReview.hint.moderator.invitations");
	}

	@Override
	public boolean isValidForm() {
		return true;
	}

	@Override
	public String getErrorMessage() {
		return null;
	}

	@Override
	public void setNextViewAction(Action action) {
		bottom.setNextViewAction(action);
	}

	@Override
	public void setPreviousViewAction(Action action) {
		bottom.setPreviousViewAction(action);
	}
	
	public void setLastExportFile(File lastDir) {
		this.lastDir = lastDir;
	}
	
	public File getLastExportFile() {
		return lastDir;
	}
}
